/*
 * Display.cpp
 *
 *  Created on: Dec 11, 2011
 *      Author: Michael Anderson
 *      Part of Team 955 Battery Cart project
 *      compiled on Eclipse with Arduino libraries and avr-gcc
 */

#include "Display.h"

Display::Display(uint8_t ID, uint8_t rs, uint8_t rw, uint8_t enable, uint8_t d0, uint8_t d1,
        uint8_t d2, uint8_t d3, uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7) {
    displayID = ID;
	locked = false;
    lcd = new LiquidCrystal(rs, rw, enable, d0, d1, d2, d3, d4, d5, d6, d7);
    lcd->begin(DISPLAY_COLS, DISPLAY_ROWS);
    clear(true);
    fullScreenActive = false;
    tickerClock = TICKER_DELAY;
}

void Display::run() {
    if (!locked) {
        uint32_t curTime = millis();
        if (getMillisSince(tickerClock, curTime) >= TICKER_DELAY) {
            for (uint8_t i = 0; i < DISPLAY_ROWS; i++) {
                scrollTicker(i);
            }
            tickerClock = curTime;
        }
    }
}

//clears the screen and the ticker
void Display::clear(bool clearTickers) {
    if (!locked) {
        if (clearTickers) {
            for (uint8_t i = 0; i < DISPLAY_ROWS; i++) {
                tickerPos[i] = 0;
                tickerStates[i] = false;
                tickerLens[i] = 0;
            }
        }
        lcd->clear();
    }
}

//Messages sent to this will not be added to the marquee strings and thus will not show up in marquees
void Display::setChar(uint8_t col, uint8_t row, char ch) {
    if (!locked) {
        lcd->setCursor(col, row);
        lcd->print(ch);
    }
}

//This prints out a whole line of text
//If the text is too long to fit on the screen it will be marquee'd (unless told not to or in full screen mode)
void Display::setLine(uint8_t row, char* str, bool allowMarquee, bool update) {
    if (!locked) {
#if ENABLE_SERIAL_MENUS
        Serial.println(str);
#endif

        if (row < DISPLAY_ROWS) {
            uint8_t strLen = getStrLength(str);
            bool tooLong = strLen > DISPLAY_COLS;

            lcd->setCursor(0, row);
            bool writeBlanks = false;

            if (!tooLong) {
                for (uint8_t i = 0; i < DISPLAY_COLS; i++) {
                    if (str[i] == 0) {
                        writeBlanks = true;
                    }
                    if (writeBlanks)
                        lcd->print(' ');
                    else
                        lcd->print(str[i]);
                    if (!fullScreenActive)
                        lines[row][i] = str[i];
                }
            }
            else if (tooLong && allowMarquee && !fullScreenActive) {
                tickerStates[row] = true;
                //Make sure we're not longer than the maximum length
                tickerLens[row] =
                        (tickerLens[row] < (MAX_TICKER_LEN - TICKER_SPACING)) ? strLen : MAX_TICKER_LEN - TICKER_SPACING;

                uint16_t lenWithSpacing = tickerLens[row] + TICKER_SPACING - 1;
                for (uint16_t i = 0; i < MAX_TICKER_LEN; i++) { //Copy over the string
                    if (i < tickerLens[row])
                        lines[row][i] = str[i];
                    else if (i < lenWithSpacing) //Leave room for a null
                        lines[row][i] = ' ';
                    else if (i == lenWithSpacing)
                        lines[row][i] = '\0';
                    else
                        break;
                }

                if (!update) {
                    tickerPos[row] = 0;
                    tickerClock = millis() + TICKER_DELAY;
                }
            }
            else if (tooLong && fullScreenActive) {
                tickerStates[row] = false;

                for (uint8_t i = 0; i < DISPLAY_COLS; i++) {
                    if (i < (DISPLAY_COLS - 1)) {
                        lines[row][i] = str[i];
                    }
                    else if (i == (DISPLAY_COLS - 1)) {
                        lines[row][i] = '\0';
                    }
                    else
                        break;
                }
            }
            else if (tooLong) {
                tickerStates[row] = false;

                for (uint8_t i = 0; i < DISPLAY_COLS; i++) {
                    if (i < (DISPLAY_COLS - 1)) {
                        lines[row][i] = str[i];
                        lcd->print(str[i]);
                    }
                    else if (i == (DISPLAY_COLS - 1)) {
                        lines[row][i] = '\0';
                        lcd->print(' ');
                    }
                    else
                        lcd->print(' ');
                }
            }
        }
    }
}

//Scrolls a line of text horizontally if it is longer than a row
//Technically, though, it can also marquee several instances of a short string at once
void Display::scrollTicker(uint8_t row) {
    if (tickerStates[row] && !fullScreenActive) { //Make sure the ticker is enabled for this row and full screen is inactive
        lcd->setCursor(0, row);

        uint16_t curPos = tickerPos[row];
        uint16_t len = strlen(lines[row]);

        //Loop from the beginning of the area on the screen to whatever fits on the screen
        for (uint8_t i = 0; i < DISPLAY_COLS; i++) {
            if (curPos >= len) { //If we reach the end of the ticker restart at the beginning
                curPos = 0;
                continue;
            }
            lcd->print(lines[row][curPos]);
            curPos++;
        }

        tickerPos[row] = (tickerPos[row] + 1) % len;
    }
}
//duh
uint16_t Display::getStrLength(char* str) {
    uint16_t i = 0;
    while (true) {
        if (!str[i])
            break;
        i++;

    }
    return i;
}

void Display::lock() { //locks the current display so the information on the screen does not change
    locked = true;
}

void Display::unlock() {
    locked = false;
}

uint8_t Display::getID() {
    return displayID;
}

//why is this a function?
void Display::setFullScreen(bool setting) {
    fullScreenActive = setting;
}

//why is this a function?
bool Display::fullScreenIsActive() {
    return fullScreenActive;
}
